/*
EvoGen, Evolutionary Geneura is a framework for simulating distributed evolutionary computation experiments
Copyright (C) 2008 Junta de Andalucia CICE project P06-TIC-02025

This file is part of EvoGen.

EvoGen is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

EvoGen is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with EvoGen; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

contact:  http://geneura.ugr.es, https://forja.rediris.es/svn/geneura/evogen
*/
package mirabilis.org.individuals;

import mirabilis.org.config.Configuration;



public abstract class Individual implements IComparator,Comparable<Individual>{
	protected IChromosome chr;
	//For sandpile mutation
	protected double[] _height=null;
	protected double[] _velocity=null;
	protected double _distance2father=0;
	protected Individual _father,_z1,_z2;
	
	public static boolean _min = true;
	
	protected double fitness;
	protected double coop_fitness;
	
	protected int _timesselected=0;
	protected int _timesampled=0;
	
	public Individual() {
	}

	public IChromosome getChr() {
		return chr;
	}
	
	public abstract double distance(Individual ind);

	public void setChr(IChromosome chr) {
		this.chr = chr;
	}

	public double getFitness() {
		return fitness;
	}

	public void setFitness(double fitness) {
		this.fitness = fitness;
	}
	
	public void setCoopFitness(double fitness){
		this.coop_fitness = fitness;
	}
	
	public double getCoopFitness(){
		return coop_fitness;
	}
	
	public abstract Object clone();
	
	public boolean better(Individual ind) {
		return (_min) ? ind.fitness > this.fitness: ind.fitness < this.fitness;
	}
	
	public boolean coopbetter(Individual ind) {
		return (_min) ? ind.coop_fitness > this.coop_fitness: ind.coop_fitness < this.coop_fitness;
	}
	
	public boolean worst(Individual ind) {
		return (_min) ? ind.fitness < this.fitness: ind.fitness > this.fitness;
	}
	
	public boolean coopworst(Individual ind) {
		return (_min) ? ind.coop_fitness < this.coop_fitness: ind.coop_fitness > this.coop_fitness;
	}
	
	public void copyFitnessToCoopFitness(){
		coop_fitness = fitness; 
	}
	
	public void decrement_coop_fitness(double decrement){
		coop_fitness -= decrement;
	}
	
	public boolean value_reached(double value){
		return (_min) ? this.fitness <= value:  this.fitness >= value;
	}
	
	public boolean equals(Object obj) {
	if (obj instanceof Individual) {
		Individual ind = (Individual) obj;
		if(ind.fitness == this.fitness)
			if (!ind.chr.equals(this.chr)) return false;
		else return false;
	}else return false;
	return true;
	}
	
	public int compareTo(Individual o) {
		// TODO Auto-generated method stub
		if (Configuration.minimization){
			return (int)(this.coop_fitness - o.coop_fitness);
		}else
			return (int)(o.coop_fitness - this.coop_fitness);
		
	}
	
	public void updatesandpile (Individual father){

		double deltaf = Math.abs(this.fitness - father.fitness);
		double summ = 0;
		for (int i=0;i<chr.getLength();i++){
			summ += Math.abs(velocity(i,father));
		}
		for (int i=0;i<chr.getLength();i++){
			_velocity[i]= this.velocity(i,father);
			_height[i] = Math.abs(_velocity[i]) * deltaf/(summ*1.0);	
		}
		_distance2father = this.distance(father);
	}
	
	public void set_nearestneighbors(Individual father, Individual z1, Individual z2){
		_father = father;
		this._distance2father = this.distance(father);
		_z1 = z1;
		_z2 = z2;
	}
	
	public double estimate_height(int index){
		if (_distance2father==0){
			return Double.MAX_VALUE;
		}else{
			double divisor = 1.0/_distance2father;
			double dividend = divisor + 1.0/this.distance(_z1) + 1.0/this.distance(_z2);
			return (divisor/dividend) * _father.get_height(index);
		}
	}
	
	public double delta_shift(int index){
		return (_z1.get_height(index)*_z1.get_velocity(index) + _z2.get_height(index)*_z2.get_velocity(index))/(_z1.get_height(index)+_z2.get_height(index));
	}
	
	public double get_height(int index) {
		return _height[index];
	}

	public double get_velocity(int index) {
		return _velocity[index];
	}
	
	public Individual get_z1(){
		return _z1;
	}
	
	public Individual get_z2(){
		return _z2;
	}


	private double velocity(int index, Individual previousindividual){
		return previousindividual.chr.asdouble()[index] - chr.asdouble()[index]; 
	}
	
	public int get_timesselected(){
		return _timesselected;
	}
	
	public int get_timesampled(){
		return _timesampled;
	}
	
	
	public void increase_timesselected(){
		_timesselected++;
	}
	
	public void increase_timessampled(){
		_timesampled++;
	}
	
	public void reset_selection(){
		_timesselected=0;
		_timesampled=0;
	}
	
}
